package com.flansmod.common.guns;

import com.flansmod.common.FlansMod;
import com.flansmod.common.guns.item.ItemBullet;
import com.flansmod.common.guns.raytracing.FlansModRaytracer;
import com.flansmod.common.guns.type.AAGunType;
import com.flansmod.common.guns.type.BulletType;
import com.flansmod.common.hook.MCHeliUtil;
import com.flansmod.common.mob.EntitySoldier;
import com.flansmod.common.network.PacketAAGunAngles;
import com.flansmod.common.network.PacketAPSMarker;
import com.flansmod.common.network.PacketMGFire;
import com.flansmod.common.network.PacketPlaySound;
import com.flansmod.common.teams.TeamsManager;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import org.lwjgl.input.Mouse;

import java.util.List;

public class EntityAAGun extends Entity implements IEntityAdditionalSpawnData, ILockable {

    /**
     * Gun angles
     */
    public float gunYaw, gunPitch;
    /**
     * Prev gun angles
     */
    public float prevGunYaw, prevGunPitch;
    public float[] barrelRecoil;
    public AAGunType type;
    public Entity towedByEntity;
    public ItemStack[] ammo; // One per barrel
    public int reloadTimer;
    public int currentBarrel; // For cycling through firing each barrel
    public boolean mouseHeld;
    public boolean wasShooting;
    public int shootcnt = 1;
    public int shootTimeCount = 0;
    /**
     * Stops the sentry shooting whoever placed it or their teammates
     */
    public EntityPlayer placer = null;
    /**
     * For getting the placer after a reload
     */
    public String placerName = null;
    /**
     * The sentry's current target
     */
    public Entity target = null;
    public int ticksSinceUsed = 0;
    public int ticksActiveDefenceActivated = 0;
    public int adsWorkTick = 0;
    public int adsShootNum = 0;
    public boolean adsWorking = false;
    public int restTime = 0;
    private int sUpdateTime;
    private double sPosX;

    //Sentry stuff
    private double sPosY;
    private double sPosZ;
    private double sYaw;
    private double sPitch;
    private int health;
    private int shootDelay;
    private boolean detonated;

    public EntityAAGun(World world) {
        super(world);
        preventEntitySpawning = true;
        setSize(1.0F, 2.0F);
        yOffset = 0F;
        gunYaw = 0;
        gunPitch = 0;
        shootDelay = 0;
        ignoreFrustumCheck = true;
        renderDistanceWeight = 20000D;
    }

    public EntityAAGun(World world, AAGunType type1, double d, double d1, double d2, EntityPlayer p) {
        this(world);
        placer = p;
        placerName = p.getCommandSenderName();
        type = type1;
        initType();
        setPosition(d, d1, d2);
    }

    @Override
    public void setPosition(double d, double d1, double d2) {
        posX = d;
        posY = d1;
        posZ = d2;
        float f = width / 2.0F;
        float f1 = height;
        boundingBox.setBounds(d - f, (d1 - yOffset) + ySize, d2 - f, d + f, (d1 - yOffset) + ySize + f1, d2 + f);
    }

    @Override
    public void setPositionAndRotation2(double d, double d1, double d2, float f, float f1, int i) {
        sPosX = d;
        sPosY = d1;
        sPosZ = d2;
        sYaw = f;
        sPitch = f1;
        sUpdateTime = i;
    }

    public void initType() {
        health = type.health;
        barrelRecoil = new float[type.numBarrels];
        ammo = new ItemStack[type.numBarrels];
    }

    @Override
    protected void entityInit() {
    }

    @Override
    public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) {
    }

    @Override
    public void applyEntityCollision(Entity entity) {
        //if(entity != riddenByEntity)
        //super.applyEntityCollision(entity);
    }

    @Override
    public AxisAlignedBB getCollisionBox(Entity entity) {
        return entity.boundingBox;
    }

    @Override
    public AxisAlignedBB getBoundingBox() {
        return boundingBox;
    }

    @Override
    public boolean canBePushed() {
        return false;
    }

    @Override
    public double getMountedYOffset() {
        return 0D;
    }

    public void setMouseHeld(boolean held) {
        mouseHeld = held;
    }

    @Override
    public boolean attackEntityFrom(DamageSource damagesource, float i) {
        if (damagesource.damageType.equals("player")) {
            Entity player = damagesource.getEntity();
            if (player == riddenByEntity) {

            } else if (riddenByEntity != null) {
                return riddenByEntity.attackEntityFrom(damagesource, i);
            } else if (TeamsManager.canBreakGuns && ((damagesource.getEntity() instanceof EntityPlayer && ((EntityPlayer) damagesource.getEntity()).capabilities.isCreativeMode) || TeamsManager.survivalCanBreakVehicles)) {
                if (!worldObj.isRemote && damagesource.getEntity() instanceof EntityPlayer) {
                    FlansMod.log("Player %s broke AA Gun %s (%d) at (%f, %f, %f)", ((EntityPlayerMP) damagesource.getEntity()).getDisplayName(), type.shortName, getEntityId(), posX, posY, posZ);
                }
                setDead();
            }
        } else {
            setBeenAttacked();
            health -= i;
            if (!worldObj.isRemote && health <= 0) {
                detonated = true;
            }
        }
        return true;
    }

    public Vec3 rotate(double x, double y, double z) {
        double cosYaw = Math.cos(180F - gunYaw * 3.14159265F / 180F);
        double sinYaw = Math.sin(180F - gunYaw * 3.14159265F / 180F);
        double cosPitch = Math.cos(gunPitch * 3.14159265F / 180F);
        double sinPitch = Math.sin(gunPitch * 3.14159265F / 180F);

        double newX = x * cosYaw + (y * sinPitch + z * cosPitch) * sinYaw;
        double newY = y * cosPitch - z * sinPitch;
        double newZ = -x * sinYaw + (y * sinPitch + z * cosPitch) * cosYaw;

        return Vec3.createVectorHelper(newX, newY, newZ);
    }

    @Override
    public boolean canBeCollidedWith() {
        return !isDead;
    }

    @Override
    public void onUpdate() {
        super.onUpdate();

        if (type == null) {
            FlansMod.log("EntityAAGun.onUpdate() Error: AAGunType is null (" + this + ")");
            setDead();
            return;
        }

        if (detonated) {
            new FlansModExplosion(worldObj, this, placer, type, posX, posY, posZ,
                    2, 1, false, false,
                    1, 1, 1, 1, 0, 0);
            setDead();
        }

        prevGunYaw = gunYaw;
        prevGunPitch = gunPitch;

        ticksSinceUsed++;
        if (TeamsManager.aaLife > 0 && ticksSinceUsed > TeamsManager.aaLife * 20) {
            setDead();
        }

        if (riddenByEntity != null) {
            ticksSinceUsed = 0;
            gunYaw = riddenByEntity.rotationYaw - 90;
            gunPitch = riddenByEntity.rotationPitch;
        }

        if (gunPitch > type.bottomViewLimit)
            gunPitch = type.bottomViewLimit;
        if (gunPitch < -type.topViewLimit)
            gunPitch = -type.topViewLimit;

        for (int i = 0; i < type.numBarrels; i++)
            barrelRecoil[i] *= 0.9F;

        if (shootDelay > 0)
            shootDelay--;

        // Sentry stuff
        if (isSentry()) {
            if (target != null && target.isDead)
                target = null;
            //Find a new target if we don't currently have one
            if (ticksExisted % type.targetAcquireInterval == 0) {
                Entity previousTarget = target;
                target = getValidTarget();
                if (target != null && target != previousTarget) {
                    PacketPlaySound.sendSoundPacket(posX, posY, posZ, 30, dimension, type.lockOnSound, true);
                }
            }
            if (target != null) {
                double dX = target.posX - posX;
                //double dY = target.posY - (posY + 1.5F);
                double dY = target.posY - posY;
                double dZ = target.posZ - posZ;

                double distanceToTarget = Math.sqrt(dX * dX + dY * dY + dZ * dZ);

                if (distanceToTarget > type.targetRange)
                    target = null;
                else if (!type.canShootHomingMissile) {
                    float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                    float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;

                    float turnSpeed = 0.25F;

                    gunYaw += (newYaw - gunYaw) * turnSpeed;
                    gunPitch += (newPitch - gunPitch) * turnSpeed;
                } else {
                    float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                    float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                    gunYaw = newYaw;
                    gunPitch = newPitch;
                }
            } else if (type.autoScan) {
                gunYaw += type.autoScanSpeed;
            }
        }

        if (type.isActiveDefence && !type.isAdvancedActiveDefence) {

            ticksActiveDefenceActivated++;
            if (ticksActiveDefenceActivated == type.activeDefenceReloadTick) {
                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.reloadSound, false);
            }

            if (!worldObj.isRemote) {
                List list = worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.expand(type.targetRange / 4, type.targetRange / 4, type.targetRange / 4));
                for (Object obj : list) {

                    Entity entity = (Entity) obj;
                    if (entity instanceof EntityBullet && ((EntityBullet) entity).type.canBeDestructByAPS) {
                        EntityBullet bullet = (EntityBullet) entity;

                        if (bullet.owner instanceof EntityPlayer && !((EntityPlayer) bullet.owner).isOnSameTeam(this.placer)) {
                            double dX = bullet.posX - posX;
                            double dY = bullet.posY - (posY + 1.5F);
                            double dZ = bullet.posZ - posZ;
                            float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                            float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                            float turnSpeed = 0.5F;
                            gunYaw += (newYaw - gunYaw) * turnSpeed;
                            gunPitch += (newPitch - gunPitch) * turnSpeed;

                            if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                bullet.setDead();
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) bullet.owner);
                                ticksActiveDefenceActivated = 0;
                            }
                        }

                    } else if (entity instanceof EntityGrenade && ((EntityGrenade) entity).type.canBeDestructByAPS) {
                        EntityGrenade bullet = (EntityGrenade) entity;

                        if (bullet.thrower instanceof EntityPlayer && !bullet.thrower.isOnSameTeam(this.placer)) {
                            double dX = bullet.posX - posX;
                            double dY = bullet.posY - (posY + 1.5F);
                            double dZ = bullet.posZ - posZ;
                            float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                            float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                            float turnSpeed = 0.5F;
                            gunYaw += (newYaw - gunYaw) * turnSpeed;
                            gunPitch += (newPitch - gunPitch) * turnSpeed;

                            if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                bullet.setDead();
                                new FlansModExplosion(worldObj, this, placer, type, bullet.posX, bullet.posY, bullet.posZ,
                                        2, 1, false,false,
                                        1, 1, 1, 1, 0, 0);
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) bullet.thrower);
                                ticksActiveDefenceActivated = 0;
                            }
                        }
                    } else if (MCHeliUtil.isMCHRocket(entity) || MCHeliUtil.isMCHMissile(entity)) {
                        Entity player = (Entity) MCHeliUtil.getBulletShootingEntity_Unsafe(entity);
                        if (player instanceof EntityPlayer && !((EntityPlayer) player).isOnSameTeam(this.placer)) {
                            double dX = entity.posX - posX;
                            double dY = entity.posY - (posY + 1.5F);
                            double dZ = entity.posZ - posZ;
                            float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                            float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                            float turnSpeed = 0.5F;
                            gunYaw += (newYaw - gunYaw) * turnSpeed;
                            gunPitch += (newPitch - gunPitch) * turnSpeed;

                            if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                entity.setDead();
                                new FlansModExplosion(worldObj, this, placer, type, entity.posX, entity.posY, entity.posZ,
                                        3, 1, false,false,
                                        1, 1, 1, 1, 0, 0);
                                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) player);
                                ticksActiveDefenceActivated = 0;
                            }
                        }
                    }
                }
            }
        } else if (type.isAdvancedActiveDefence) {
            ticksActiveDefenceActivated++;
            if (ticksActiveDefenceActivated == type.activeDefenceReloadTick) {
                PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.reloadSound, false);
            }

            if (!worldObj.isRemote) {
                List list = worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.expand(type.targetRange / 4, type.targetRange / 4, type.targetRange / 4));

                if (list.size() > 0) {
                    for (Object obj : list) {

                        Entity entity = (Entity) obj;
                        if (entity instanceof EntityBullet && ((EntityBullet) entity).type.canBeDestructByAPS) {
                            EntityBullet bullet = (EntityBullet) entity;

                            if (bullet.owner instanceof EntityPlayer && !((EntityPlayer) bullet.owner).isOnSameTeam(this.placer)) {
                                double dX = bullet.posX - posX;
                                double dY = bullet.posY - (posY + 1.5F);
                                double dZ = bullet.posZ - posZ;
                                float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                                float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                                float turnSpeed = 0.5F;
                                gunYaw += (newYaw - gunYaw) * turnSpeed;
                                gunPitch += (newPitch - gunPitch) * turnSpeed;

                                if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                    adsWorking = true;
                                    bullet.setDead();
                                    PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                    FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) bullet.owner);
                                    adsShootNum++;
                                    if(adsShootNum == 1) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.startSound, false);
                                    }
                                    if (adsShootNum >= type.advancedActiveDefenceShootCount) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                        ticksActiveDefenceActivated = 0;
                                        adsShootNum = 0;
                                        adsWorking = false;
                                    }
                                }
                            }
                        } else if (entity instanceof EntityGrenade && ((EntityGrenade) entity).type.canBeDestructByAPS) {
                            EntityGrenade bullet = (EntityGrenade) entity;

                            if (bullet.thrower instanceof EntityPlayer && !bullet.thrower.isOnSameTeam(this.placer)) {
                                double dX = bullet.posX - posX;
                                double dY = bullet.posY - (posY + 1.5F);
                                double dZ = bullet.posZ - posZ;
                                float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                                float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                                float turnSpeed = 0.5F;
                                gunYaw += (newYaw - gunYaw) * turnSpeed;
                                gunPitch += (newPitch - gunPitch) * turnSpeed;

                                if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                    adsWorking = true;
                                    bullet.setDead();
                                    new FlansModExplosion(worldObj, this, placer, type, bullet.posX, bullet.posY, bullet.posZ,
                                            2, 1, false,false,
                                            1, 1, 1, 1, 0, 0);
                                    PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                    FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) bullet.thrower);
                                    adsShootNum++;
                                    if(adsShootNum == 1) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.startSound, false);
                                    }
                                    if (adsShootNum >= type.advancedActiveDefenceShootCount) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                        ticksActiveDefenceActivated = 0;
                                        adsShootNum = 0;
                                        adsWorking = false;
                                    }
                                }
                            }
                        } else if (MCHeliUtil.isMCHRocket(entity) || MCHeliUtil.isMCHMissile(entity)) {
                            Entity player = (Entity) MCHeliUtil.getBulletShootingEntity_Unsafe(entity);
                            if (player instanceof EntityPlayer && !((EntityPlayer) player).isOnSameTeam(this.placer)) {
                                double dX = entity.posX - posX;
                                double dY = entity.posY - (posY + 1.5F);
                                double dZ = entity.posZ - posZ;
                                float newYaw = 180F + (float) Math.atan2(dZ, dX) * 180F / 3.14159F;
                                float newPitch = -(float) Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F;
                                float turnSpeed = 0.5F;
                                gunYaw += (newYaw - gunYaw) * turnSpeed;
                                gunPitch += (newPitch - gunPitch) * turnSpeed;

                                if (ticksActiveDefenceActivated > type.activeDefenceReloadTick) {
                                    adsWorking = true;
                                    entity.setDead();
                                    new FlansModExplosion(worldObj, this, placer, type, entity.posX, entity.posY, entity.posZ,
                                            2, 1, false,false,
                                            1, 1, 1, 1, 0, 0);
                                    PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.shootSound, false);
                                    FlansMod.getPacketHandler().sendTo(new PacketAPSMarker(), (EntityPlayerMP) player);
                                    adsShootNum++;
                                    if(adsShootNum == 1) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.startSound, false);
                                    }
                                    if (adsShootNum >= type.advancedActiveDefenceShootCount) {
                                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                                        ticksActiveDefenceActivated = 0;
                                        adsShootNum = 0;
                                        adsWorking = false;
                                    }
                                }
                            }
                        }
                    }
                }

                if (adsWorking) {
                    adsWorkTick++;
                    if (adsWorkTick >= type.advancedActiveDefenceWorkTick) {
                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 20, dimension, type.offlineSound, false);
                        ticksActiveDefenceActivated = 0;
                        adsWorkTick = 0;
                        adsShootNum = 0;
                        adsWorking = false;
                    }
                }
            }
        }

        // apply gravity

//        if (!onGround && !worldObj.isRemote)
//            motionY -= 9.8D / 400D;
        motionY -= 9.81D / 400D;

        // update motion
        motionX *= 0.5;
        motionZ *= 0.5;
        moveEntity(motionX, motionY, motionZ);

        if (worldObj.isRemote && riddenByEntity != null && riddenByEntity == FMLClientHandler.instance().getClient().thePlayer) {
            checkForShooting();
        }

        if (worldObj.isRemote) {
            if (sUpdateTime > 0) {
                double d1 = posX + (sPosX - posX) / sUpdateTime;
                double d5 = posY + (sPosY - posY) / sUpdateTime;
                double d9 = posZ + (sPosZ - posZ) / sUpdateTime;
                double d12;
                for (d12 = sYaw - rotationYaw; d12 < -180D; d12 += 360D) {
                }
                for (; d12 >= 180D; d12 -= 360D) {
                }
                rotationYaw += d12 / sUpdateTime;
                rotationPitch += (sPitch - rotationPitch) / sUpdateTime;
                sUpdateTime--;
                setPosition(d1, d5, d9);
                setRotation(rotationYaw, rotationPitch);
            }
            return;
        }

        if (riddenByEntity != null && riddenByEntity.isDead) {
            riddenByEntity = null;
        }

        // Decrement the reload timer and reload
        if (reloadTimer > 0)
            reloadTimer--;
            //If it is 0 or less, go ahead and reload
        else {
            for (int i = 0; i < type.numBarrels; i++) {
                if (ammo[i] != null && ammo[i].getItemDamage() == ammo[i].getMaxDamage()) {
                    ammo[i] = null;
                    // Scrap metal output?
                }
                if (ammo[i] == null && riddenByEntity != null && riddenByEntity instanceof EntityPlayer) {
                    int slot = findAmmo(((EntityPlayer) riddenByEntity));
                    if (slot >= 0) {
                        ammo[i] = ((EntityPlayer) riddenByEntity).inventory.getStackInSlot(slot);
                        if (!((EntityPlayer) riddenByEntity).capabilities.isCreativeMode)
                            ((EntityPlayer) riddenByEntity).inventory.decrStackSize(slot, 1);
                        reloadTimer = type.reloadTime;
                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 30, dimension, type.reloadSound, false);
                    }
                }
            }
        }

        if (shootcnt > 0 && shootcnt < 10)
            shootcnt++;

        if (restTime > 0)
            restTime--;

        if (!worldObj.isRemote && reloadTimer <= 0 && shootDelay <= 0) {
            if (mouseHeld && riddenByEntity != null && riddenByEntity instanceof EntityPlayer) {
                EntityPlayer player = (EntityPlayer) riddenByEntity;
                for (int j = 0; j < type.numBarrels; j++) {
                    if (shootDelay <= 0 && ammo[j] != null && (!type.fireAlternately || type.fireAlternately && currentBarrel == j)) {
                        // Fire
                        BulletType bullet = BulletType.getBullet(ammo[j].getItem());
                        if (!((EntityPlayer) riddenByEntity).capabilities.isCreativeMode)
                            ammo[j].damageItem(1, player);
                        shootDelay = type.shootDelay;
                        barrelRecoil[j] = type.recoil;
                        float shootSpeed = (bullet == null ? 1F : bullet.speedMultiplier) * type.bulletSpeed;
                        worldObj.spawnEntityInWorld(((ItemBullet) ammo[j].getItem()).getEntity(worldObj, rotate(type.barrelX[currentBarrel] / 16D - type.barrelZ[currentBarrel] / 16D, type.barrelY[currentBarrel] / 16D, type.barrelX[currentBarrel] / 16D + type.barrelZ[currentBarrel] / 16D).addVector(posX, posY, posZ), gunYaw + 90F, gunPitch, player, type.accuracy, type.damage, shootSpeed, ammo[j].getItemDamage(), type));
                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 50, dimension, type.shootSound, true);
                    }
                }
                currentBarrel = (currentBarrel + 1) % type.numBarrels;
            } else if (target != null && shootcnt > 5 && restTime == 0) {
                for (int j = 0; j < type.numBarrels; j++) {
                    int ammoSlot = j;
                    if (type.shareAmmo)
                        ammoSlot = 0;
                    if (shootDelay <= 0 && ammo[ammoSlot] != null && (!type.fireAlternately || type.fireAlternately && currentBarrel == ammoSlot)) {
                        // Fire
                        BulletType bullet = BulletType.getBullet(ammo[ammoSlot].getItem());
                        ammo[ammoSlot].setItemDamage(ammo[ammoSlot].getItemDamage() + 1);
                        shootDelay = type.shootDelay;
                        barrelRecoil[ammoSlot] = type.recoil;
                        float shootSpeed = (bullet == null ? 1F : bullet.speedMultiplier) * type.bulletSpeed;
                        worldObj.spawnEntityInWorld(((ItemBullet) ammo[ammoSlot].getItem()).getEntity(worldObj, rotate(type.barrelX[currentBarrel] / 16D - type.barrelZ[currentBarrel] / 16D, type.barrelY[currentBarrel] / 16D, type.barrelX[currentBarrel] / 16D + type.barrelZ[currentBarrel] / 16D).addVector(posX, posY + 1.5F, posZ), gunYaw + 90F, gunPitch, placer, type.accuracy, type.damage, shootSpeed, ammo[ammoSlot].getItemDamage(), type));
                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 50, dimension, type.shootSound, true);

                        if (shootTimeCount >= type.countExplodeAfterShoot - 1 && type.countExplodeAfterShoot != -1 && !worldObj.isRemote) {
                            new FlansModExplosion(worldObj, this, placer, type, posX, posY, posZ,
                                    2, 1, false,false,
                                    1, 1, 1, 1, 0, 0);
                            this.setDead();
                        }
                        if (shootTimeCount >= type.countRestAfterShoot && type.countRestAfterShoot != -1 && !worldObj.isRemote) {
                            shootTimeCount = 0;
                            restTime = type.restTime;
                        }
                        shootTimeCount++;
                    }
                }
                currentBarrel = (currentBarrel + 1) % type.numBarrels;
            }
        }
        if (!worldObj.isRemote) {
            FlansMod.getPacketHandler().sendToAllAround(new PacketAAGunAngles(this), posX, posY, posZ, 80F, dimension);
        }
    }

    public boolean isSentry() {
        return type.targetMobs || type.targetPlayers || type.targetPlanes || type.targetVehicles || type.targetMechas;
    }

    public Entity getValidTarget() {
        if (worldObj.isRemote)
            return null;
        if (placer == null && placerName != null)
            placer = worldObj.getPlayerEntityByName(placerName);
        if (placer == null)
            return null;
        for (Object obj : worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.expand(type.targetRange, type.targetRange, type.targetRange))) {
            Entity candidateEntity = (Entity) obj;

            if ((type.targetMobs && candidateEntity instanceof EntityMob)
                    || (type.targetPlayers && candidateEntity instanceof EntityPlayer)
                    || (type.targetPlayers && candidateEntity instanceof EntitySoldier && !((EntitySoldier) candidateEntity).isOnSameTeam(placer))
                    || (type.targetPlanes && isEnemyPlane(candidateEntity, placer))
                    || (type.targetVehicles && isEnemyVehicle(candidateEntity, placer)
                    || (type.targetDriveables && isEnemyDriveable(candidateEntity, placer)))
            ) {
                //Check that this entity is actually in range and visible
                if (candidateEntity.getDistanceToEntity(this) < type.targetRange
                        && candidateEntity.getDistanceToEntity(this) > type.minimumTargetRange) {
                    if (!isTargetInViewLimit(candidateEntity))
                        continue;
                    if (FlansModRaytracer.hasBlock(worldObj, Vec3.createVectorHelper(posX, posY + 1.5F, posZ), Vec3.createVectorHelper(candidateEntity.posX, candidateEntity.posY + 1, candidateEntity.posZ)))
                        continue;
                    if (candidateEntity instanceof EntityPlayer) {
                        if (candidateEntity == placer || candidateEntity.getCommandSenderName().equals(placerName))
                            continue;
                        if (((EntityLivingBase) candidateEntity).isOnSameTeam(placer))
                            continue;
                    }
                    return candidateEntity;
                }
            }
        }
        return null;
    }

    public boolean isTargetInViewLimit(Entity target) {
        if (target == null)
            return false;
        double dX = target.posX - posX;
        double dY = target.posY - (posY + 1.5F);
        double dZ = target.posZ - posZ;
        double targetPitch = Math.atan2(dY, Math.sqrt(dX * dX + dZ * dZ)) * 180F / 3.14159F
                * (dY > 0 ? 1 : -1);
        return targetPitch < type.topViewLimit && targetPitch < type.bottomViewLimit;
    }


    @SideOnly(Side.CLIENT)
    private void checkForShooting() {
        //Send a packet!
        if (Mouse.isButtonDown(0) && !wasShooting && !FlansMod.proxy.isScreenOpen()) {
            FlansMod.getPacketHandler().sendToServer(new PacketMGFire(true));
            wasShooting = true;
        } else if (!Mouse.isButtonDown(0) && wasShooting) {
            FlansMod.getPacketHandler().sendToServer(new PacketMGFire(false));
            wasShooting = false;
        }
    }

    @Override
    public void setDead() {
        super.setDead();
        // Drop gun
        if (worldObj.isRemote)
            return;
        if (type.isDropThis)
            dropItem(type.getItem(), 1);
        // Drop ammo boxes
        for (ItemStack stack : ammo) {
            if (stack != null)
                entityDropItem(stack, 0.5F);
        }
    }

    @Override
    public void updateRiderPosition() {
        if (riddenByEntity == null) {
            return;
        }
        double x = type.gunnerX / 16D;
        double y = type.gunnerY / 16D;
        double z = type.gunnerZ / 16D;

        double cosYaw = Math.cos((-gunYaw / 180D) * 3.1415926535897931D);
        double sinYaw = Math.sin((-gunYaw / 180D) * 3.1415926535897931D);
        double cosPitch = Math.cos((gunPitch / 180D) * 3.1415926535897931D);
        double sinPitch = Math.sin((gunPitch / 180D) * 3.1415926535897931D);

        double x2 = x * cosYaw + z * sinYaw;
        double z2 = -x * sinYaw + z * cosYaw;

        riddenByEntity.setPosition(posX + x2, posY + y, posZ + z2);
    }

    @Override
    protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
        if (type == null) {
            FlansMod.log("EntityAAGun.writeEntityToNBT() Error: AAGunType is null (" + this + ")");
            setDead();
            return;
        }

        nbttagcompound.setString("Type", type.shortName);
        nbttagcompound.setInteger("Health", health);
        nbttagcompound.setFloat("RotationYaw", rotationYaw);
        nbttagcompound.setFloat("RotationPitch", rotationPitch);
        for (int i = 0; i < type.numBarrels; i++) {
            if (ammo[i] != null)
                nbttagcompound.setTag("Ammo " + i, ammo[i].writeToNBT(new NBTTagCompound()));
        }
        nbttagcompound.setString("Placer", placer.getCommandSenderName());
    }

    @Override
    protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
        type = AAGunType.getAAGun(nbttagcompound.getString("Type"));
        if (type == null) {
            FlansMod.log("EntityAAGun.readEntityFromNBT() Error: AAGunType is null (" + this + ")");
            setDead();
            return;
        }

        initType();
        health = nbttagcompound.getInteger("Health");
        rotationYaw = nbttagcompound.getFloat("RotationYaw");
        rotationPitch = nbttagcompound.getFloat("RotationPitch");
        for (int i = 0; i < type.numBarrels; i++) {
            ammo[i] = ItemStack.loadItemStackFromNBT(nbttagcompound.getCompoundTag("Ammo " + i));
        }
        placerName = nbttagcompound.getString("Placer");
    }

    @Override
    public float getShadowSize() {
        return 1.0F;
    }

    @Override
    public boolean interactFirst(EntityPlayer entityplayer) //interact : change back when Forge updates
    {
        // Player right clicked on gun
        // Mount gun
        if (riddenByEntity != null && (riddenByEntity instanceof EntityPlayer) && riddenByEntity != entityplayer) {
            return true;
        }
        if (!worldObj.isRemote) {
            if (riddenByEntity == entityplayer) {
                entityplayer.mountEntity(null);
                return true;
            }
            if (!isSentry())
                entityplayer.mountEntity(this);
            for (int i = 0; i < (type.shareAmmo ? 1 : type.numBarrels); i++) {
                if (ammo[i] == null) {
                    int slot = findAmmo(entityplayer);
                    if (slot >= 0) {
                        ammo[i] = entityplayer.inventory.getStackInSlot(slot).copy();
                        ammo[i].stackSize = 1;
                        if (!entityplayer.capabilities.isCreativeMode)
                            entityplayer.inventory.decrStackSize(slot, 1);
                        reloadTimer = type.reloadTime;
                        PacketPlaySound.sendSoundPacket(posX, posY, posZ, 30, dimension, type.reloadSound, false);
                    }
                }
            }
        }
        return true;
    }

    public int findAmmo(EntityPlayer player) {
        for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
            ItemStack stack = player.inventory.getStackInSlot(i);
            if (type.isAmmo(stack)) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public void writeSpawnData(ByteBuf data) {
        data.writeInt(placer == null ? 0 : placer.getEntityId());
        ByteBufUtils.writeUTF8String(data, type.shortName);
    }

    @Override
    public void readSpawnData(ByteBuf data) {
        try {
            int entityID = data.readInt();
            Entity ent = worldObj.getEntityByID(entityID);
            if (ent instanceof EntityPlayer) {
                placer = (EntityPlayer) ent;
            } else {
                FlansMod.log("Placer is not an EntityPlayer!");
                if (FlansMod.DEBUG) {
                    FlansMod.log("Entity ID " + entityID);
                }
                placer = null;
            }
            type = AAGunType.getAAGun(ByteBufUtils.readUTF8String(data));
            initType();
        } catch (Exception e) {
            FlansMod.log("Failed to retreive AA gun type from server.");
            super.setDead();
            e.printStackTrace();
        }
    }

    @Override
    public boolean canRiderInteract() {
        return false;
    }

    @Override
    public ItemStack getPickedResult(MovingObjectPosition target) {
        ItemStack stack = new ItemStack(type.item, 1, 0);
        return stack;
    }
}